package Algorithms;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import Stack.Stack;
import Stack.StackIterator;
import Templates.Operator;
import Templates.State;

public class DepthSearch<Template> {

    private boolean switcher;
    private Stack<State<Template>> btStack;
    private ArrayList<Operator<Template>> operators;
    private State<Template> initialState;
    private ArrayList<State<Template>> finalStates;
    private int operatorNumber = 0;
    private PrintWriter logWriter = null;
    private File log;

    public DepthSearch(State<Template> iState, ArrayList<State<Template>> fStates, ArrayList<Operator<Template>> opr, String log) {
        setSwitcher(false);
        btStack = new Stack<State<Template>>(100);
        initialState = iState;
        finalStates = fStates;
        if (opr != null) {
            operators = opr;
            operatorNumber = operators.size() - 1;
        }
        try {
            logWriter = new PrintWriter(new FileWriter(log));
        } catch (IOException e) {
            e.printStackTrace();
        }
        this.log = new File(log);
    }

    private boolean found(State<Template> thisState) {
        for (StackIterator<State<Template>> it = new StackIterator<State<Template>>(btStack); it.hasNext();) {
            if (thisState.equals(it.next())) {
                return true;
            }
        }
        return false;
    }

    private boolean solutionFound(State<Template> thisState) {
        for (int i = 0; i < finalStates.size(); i++) {
            if (thisState.equals(finalStates.get(i))) {
                return true;
            }
        }

        return false;
    }

    private int getIndex(Operator<Template> operator) {
        for (int i = 0; i < operators.size(); i++) {

            if (operators.get(i).getOperatorNumber() == operator.getOperatorNumber()) {
                return i;
            }
        }
        return 0;

    }

    private boolean expand(State<Template> thisState) {
        int counter = 0;
        if (isSwitcher()) {
            counter = getIndex(thisState.getRedoOperator()) + 1;
            setSwitcher(false);
        }
        if (getIndex(thisState.getCurrentOperator()) == operatorNumber) {
            counter = 0;
        }
        if (operators != null) {
            for (int i = counter; i <= operatorNumber; i++) {
                if (operators.get(i).available(thisState.getCurrentState())) {
                    State<Template> auxiliary = new State<Template>(operators.get(i).execute(thisState.getCurrentState()), operators.get(i));
                    if (!found(auxiliary)) {
                        btStack.push(auxiliary);
                        return true;
                    }
                }
            }
        }
        return false;
    }

    private boolean solve(State<Template> thisState) {

        if (!isSwitcher()) {
            setSwitcher(false);
        }
        if (thisState != null) {
            if (expand(thisState)) {
                if (solutionFound(btStack.peek())) {
                    return true;
                }
                ;
            } else {
                Operator<Template> auxiliaryOperator = btStack.peek().getCurrentOperator();
                if (btStack.size() > 1) {
                    btStack.pop();
                }
                State<Template> auxiliaryState = btStack.pop();
                auxiliaryState.setRedoOperator(auxiliaryOperator);
                btStack.push(auxiliaryState);
                setSwitcher(true);

            }
            solve(btStack.peek());
        }
        return false;
    }

    private void print(State<Template> thisState) {
        logWriter.append("\n~~~~~~~~~~~~~~~~~~~~aa~~~~~~~\n");
        logWriter.append(thisState.getCurrentOperator().toString() + "\n");
        logWriter.append('\n');
        logWriter.append(thisState.getCurrentState().toString() + "\n");
        logWriter.append('\n');


    }

    private void closeLog() {
        System.out.println("Solution found using Indepth Search... Stacktrace can be found in: " + log.getAbsolutePath());
        logWriter.close();
    }

    public void executeAlgorithm() {

        btStack.push(initialState);
        solve(initialState);
        for (StackIterator<State<Template>> it = new StackIterator<State<Template>>(btStack); it.hasNext();) {
            print(it.next());
        }
        closeLog();

    }

    private boolean setSwitcher(boolean switcher) {
        this.switcher = switcher;
        return switcher;
    }

    public boolean isSwitcher() {
        return switcher;
    }
}
